import {checkChunks, mergeChunks, uploadChunks} from "@/api/documentManage/minioManager2";
import SparkMD5 from 'spark-md5'

import {message} from "@/utils/ElmessasgeUtils";
import {formatCurrentTime} from "@/utils/dateUtils";

const CHUNK_SIZE = 50 * 1024 * 1024; // 10MB

export interface UploadFile extends File {
    md5?: string;
    chunkCount?: number;
    uploadedChunks?: Set<number>;
}

export interface UploadProgress {
    total: number;
    uploaded: number;
    percentage: number;
}

export interface MergeResponse {
    success: boolean;
    url: string;
    bucketName: string;
    path: string;
    fileName: string;
}

// 文件获取hash值
const getFileMD5 = (file: File): Promise<string> => {
    return new Promise((resolve, reject) => {
        const spark = new SparkMD5.ArrayBuffer()
        const fileReader = new FileReader()

        fileReader.onload = (e: ProgressEvent<FileReader>): void => {
            // 添加类型断言，明确指定为 ArrayBuffer
            spark.append(e.target?.result as ArrayBuffer)
            resolve(spark.end())
        }

        fileReader.onerror = () => {
            reject(new Error('File reading failed'))
        }

        fileReader.readAsArrayBuffer(file)
    })
}

export class FileUploader {
    private file: UploadFile;
    private onProgress: (progress: UploadProgress) => void;
    private onSuccess: (mergeResp: MergeResponse) => void;
    private onError: (error: string) => void;
    private isCanceled: boolean = false;
    private bucketName: string = 'public';
    private path: string = '';

    constructor(
        file: File,
        bucketName: string,
        path: string,
        onProgress: (progress: UploadProgress) => void,
        onSuccess: (mergeResp: MergeResponse) => void,
        onError: (error: string) => void
    ) {
        this.file = file as UploadFile;
        this.onProgress = onProgress;
        this.onSuccess = onSuccess;
        this.onError = onError;
        this.bucketName = bucketName;
        this.path = path;
    }

    // 开始上传
    async startUpload(): Promise<void> {
        try {
            console.log('开始上传文件:', this.file.name, '大小:', this.file.size);
            // 1. 检查文件是否已存在（秒传）
            const checkResult = await this.checkFileExists();
            console.log("检测文件结束", formatCurrentTime())
            if (checkResult.exists && checkResult.url) {
                message('文件已存在，秒传成功');
                this.onProgress({
                    total: this.file.size,
                    uploaded: this.file.size,
                    percentage: 100
                });
                this.onSuccess({
                    fileName: this.file.name,
                    path: this.path,
                    bucketName: this.bucketName,
                    success: true,
                    url: checkResult.url,
                });
                return;
            }

            // 2. 准备上传
            const totalChunks = Math.ceil(this.file.size / CHUNK_SIZE);
            this.file.chunkCount = totalChunks;
            this.file.uploadedChunks = new Set(checkResult.uploadedChunks);

            console.log(`总分片数: ${totalChunks}, 已上传分片:`, Array.from(this.file.uploadedChunks));

            let uploadedSize = checkResult.uploadedChunks.length * CHUNK_SIZE;

            // 初始进度更新
            if (uploadedSize > 0) {
                const initialPercentage = Math.round((uploadedSize / this.file.size) * 100);
                console.log('初始进度:', initialPercentage + '%');
                this.onProgress({
                    total: this.file.size,
                    uploaded: uploadedSize,
                    percentage: initialPercentage
                });
            }

            // 3. 上传分片
            for (let chunkNumber = 1; chunkNumber <= totalChunks; chunkNumber++) {
                if (this.isCanceled) {
                    console.log('上传已被取消');
                    return;
                }

                // 跳过已上传的分片
                if (this.file.uploadedChunks!.has(chunkNumber)) {
                    console.log(`跳过已上传分片: ${chunkNumber}`);
                    continue;
                }

                const start = (chunkNumber - 1) * CHUNK_SIZE;
                const end = Math.min(start + CHUNK_SIZE, this.file.size);
                const chunk = this.file.slice(start, end);
                console.log(`开始上传分片 ${chunkNumber}/${totalChunks}`);
                const success = await this.uploadChunk(chunk, chunkNumber, totalChunks);

                if (success) {
                    uploadedSize += chunk.size;
                    this.file.uploadedChunks!.add(chunkNumber);

                    const percentage = Math.round((uploadedSize / this.file.size) * 100);
                    console.log(`分片 ${chunkNumber} 上传成功, 当前进度: ${percentage}%`);

                    // 更新进度
                    this.onProgress({
                        total: this.file.size,
                        uploaded: uploadedSize,
                        percentage: percentage
                    });
                } else {
                    throw new Error(`分片 ${chunkNumber} 上传失败`);
                }
            }

            // 4. 合并分片
            const mergeRes = await this.mergeChunks(totalChunks);
            this.onSuccess(mergeRes);

        } catch (error) {
            console.error('上传过程出错:', error);
            this.onError(error instanceof Error ? error.message : '上传失败');
        }
    }

    // 取消上传
    cancelUpload(): void {
        this.isCanceled = true;
        console.log('上传已取消');
    }


    // 检查文件是否已存在
    private async checkFileExists(): Promise<{ exists: boolean; uploadedChunks: number[]; url?: string }> {
        try {
            const md5 = await getFileMD5(this.file)
            this.file.md5 = md5;

            const response: any = await checkChunks({
                bucketName: this.bucketName,
                path: this.path,
                fileMd5: md5,
                fileName: this.file.name,
                fileSize: this.file.size
            });
            if (response.data && response.data.code === 200) {
                return {
                    exists: response.data.data.exists || false,
                    uploadedChunks: response.data.data.uploadedChunks || [],
                    url: response.data.data.url
                };
            } else {
                throw new Error('检查文件接口返回异常');
            }
        } catch (error) {
            console.error('检查文件失败:', error);
            return {exists: false, uploadedChunks: []};
        }
    }

    // 上传单个分片
    private async uploadChunk(chunk: Blob, chunkNumber: number, totalChunks: number): Promise<boolean> {
        const formData = new FormData();
        formData.append('file', chunk);
        formData.append('fileMd5', this.file.md5!);
        formData.append('bucketName', this.bucketName);
        formData.append('chunkNumber', chunkNumber.toString());
        formData.append('totalChunks', totalChunks.toString());

        try {
            console.log(`上传分片 ${chunkNumber}, 大小: ${chunk.size} bytes`);

            const response: any = await uploadChunks(formData);
            console.log(`分片 ${chunkNumber} 上传响应:`, response);

            if (response.data && response.data.code === 200) {
                return response.data.data.success === true;
            } else {
                console.error(`分片 ${chunkNumber} 上传失败，响应码:`, response.data?.code);
                return false;
            }
        } catch (error) {
            console.error(`上传分片 ${chunkNumber} 失败:`, error);
            return false;
        }
    }

    // 合并分片
    private async mergeChunks(totalChunks: number): Promise<MergeResponse> {
        try {

            let param = {
                bucketName: this.bucketName,
                fileMd5: this.file.md5,
                fileName: this.file.name,
                path: this.path,
                totalChunks
            }
            console.log('开始合并分片，参数:', param);
            const response = await mergeChunks(param);
            if (response.data && response.data.code === 200) {
                if (response.data.data.success) {
                    return response.data.data;
                } else {
                    throw new Error(response.data.data.message || '合并文件失败');
                }
            } else {
                throw new Error('合并文件接口返回异常');
            }
        } catch (error) {
            console.error('合并文件失败:', error);
            throw error;
        }
    }
}